package com.ubxtech.web.handler;

import cn.hutool.core.util.StrUtil;
import com.ubxtech.annotation.ClientAuthorization;
import com.ubxtech.annotation.InnerApi;
import com.ubxtech.annotation.MsgCallback;
import com.ubxtech.annotation.RepeatSubmit;
import com.ubxtech.core.dto.MsgCallbackDTO;
import com.ubxtech.core.dto.RequestMappingDTO;
import com.ubxtech.core.dto.RequestMappingRepeatSubmitConfigDTO;
import com.ubxtech.core.plugins.RequestMappingScanService;
import com.ubxtech.core.utils.AntPathMatcherUtil;
import com.ubxtech.core.utils.SpringUtils;
import com.ubxtech.web.annotation.Permission;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import java.util.*;

/**
 * @author Victor.Xiao
 * @since 2023-09-05 14:53
 **/
@Slf4j
@Component
@ConditionalOnBean(RequestMappingScanService.class)
public class RequestMappingScanListener implements ApplicationListener<ApplicationReadyEvent> {

    List<String> ignoreApi = new ArrayList<>(Arrays.asList(
            "/swagger/**",
            "/**/api-docs/**",
            "/**/api-docs-ext/**",
            "/swagger-resources/**",
            "/webjars/**",
            "/actuator/**",
            "/error"
    ));

    @Resource
    private RequestMappingScanService requestMappingScanService;

    @Override
    public void onApplicationEvent(@NotNull ApplicationReadyEvent event) {

        try {
//            ConfigurableApplicationContext applicationContext = event.getApplicationContext();
            // 获取微服务模块名称
            String serviceId = SpringUtils.getApplicationName();
            // 所有接口映射
            RequestMappingHandlerMapping requestMappingHandlerMapping = SpringUtils.getBean(RequestMappingHandlerMapping.class);
            // 获取url与类和方法的对应信息
            Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();
            //所有的接口信息
            List<RequestMappingDTO> list = new ArrayList<>();
            for (Map.Entry<RequestMappingInfo, HandlerMethod> handlerMethodEntry : handlerMethods.entrySet()) {
                RequestMappingInfo requestMappingInfo = handlerMethodEntry.getKey();
                HandlerMethod handlerMethod = handlerMethodEntry.getValue();
                String path = "";
                // 请求路径 PatternsRequestCondition 在spring-boot 2.6有值,3.0为空
                PatternsRequestCondition patternsCondition = requestMappingInfo.getPatternsCondition();
                if (patternsCondition != null) {
                    path = patternsCondition.getPatterns().iterator().next();
                }
                else {
                    // 请求路径 在spring-boot 3.0有值
                    PathPatternsRequestCondition pathPatternsRequestCondition = requestMappingInfo.getPathPatternsCondition();
                    path = pathPatternsRequestCondition.getPatternValues().iterator().next();
                }
                if (StrUtil.isBlank(path)) {
                    log.warn("获取到接口url为空");
                    continue;
                }

                if (isIgnore(path)) {
                    continue;
                }
                RequestMappingDTO requestMappingDTO = new RequestMappingDTO();
                requestMappingDTO.setPath(path);
                Set<MediaType> mediaTypeSet = requestMappingInfo.getProducesCondition().getProducibleMediaTypes();
                MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
                for (MethodParameter params : methodParameters) {
                    if (params.hasParameterAnnotation(RequestBody.class)) {
                        mediaTypeSet.add(MediaType.APPLICATION_JSON);
                        break;
                    }
                }
                String mediaTypes = StrUtil.join(",",mediaTypeSet);
                requestMappingDTO.setMethod(mediaTypes);

                // 请求类型
                RequestMethodsRequestCondition methodsCondition = requestMappingInfo.getMethodsCondition();
                String methods = StrUtil.join(",",methodsCondition.getMethods());
                // 类名
                String className = handlerMethod.getMethod().getDeclaringClass().getName();
                // 方法名
                String methodName = handlerMethod.getMethod().getName();
                String returnName = handlerMethod.getReturnType().getParameterType().getTypeName();
                // md5码
                String md5 = DigestUtils.md5DigestAsHex((serviceId + path + methodName).getBytes());
                String description = "";
                String permissionFlag = "0";
                String callbackFlag = "0";
                String clientFlag = "0";
                Integer innerApiFlag = 0;
                Operation apiOperation = handlerMethod.getMethodAnnotation(Operation.class);
                if (apiOperation != null) {
                    description = apiOperation.summary();
                }
                // 判断是否需要权限校验
                Permission permission = handlerMethod.getMethodAnnotation(Permission.class);
                if (permission == null) {
                    //不能用handlerMethod.getClass,得到的是spring的对象
                    permission = handlerMethod.getBeanType().getAnnotation(Permission.class);
                }
                if (permission != null) {
                    permissionFlag = "1";
                }
                // 判断是否需要sign权限校验
                ClientAuthorization clientAuthorization = handlerMethod.getMethodAnnotation(ClientAuthorization.class);
                if (clientAuthorization == null) {
                    //不能用handlerMethod.getClass,得到的是spring的对象
                    clientAuthorization = handlerMethod.getBeanType().getAnnotation(ClientAuthorization.class);
                }
                if (clientAuthorization != null) {
                    clientFlag = "1";
                }

                //判断是否需要重复提交校验
                RequestMappingRepeatSubmitConfigDTO repeatSubmitConfig = new RequestMappingRepeatSubmitConfigDTO();
                requestMappingDTO.setRepeatSubmitConfig(repeatSubmitConfig);
                RepeatSubmit repeatSubmit = handlerMethod.getMethodAnnotation(RepeatSubmit.class);
                if (repeatSubmit == null) {
                    //不能用handlerMethod.getClass,得到的是spring的对象
                    repeatSubmit = handlerMethod.getBeanType().getAnnotation(RepeatSubmit.class);
                }
                if (repeatSubmit != null) {
                    repeatSubmitConfig.setTime(repeatSubmit.time());
                    repeatSubmitConfig.setType(repeatSubmit.type().getCode());
                }

                // 判断是否是内部接口
                InnerApi innerApi = handlerMethod.getMethodAnnotation(InnerApi.class);
                if (innerApi == null) {
                    //不能用handlerMethod.getClass,得到的是spring的对象
                    innerApi = handlerMethod.getBeanType().getAnnotation(InnerApi.class);
                }
                if (innerApi != null) {
                    innerApiFlag = 1;

                }

                //消息回调方法,必须要有注解,而且参数要是MsgCallbackDTO
                MsgCallback msgCallback = handlerMethod.getMethodAnnotation(MsgCallback.class);
                if (msgCallback != null) {
                    requestMappingDTO.setCallbackMsgType(msgCallback.type().getCode());
                    requestMappingDTO.setCallbackMsgSubTopic(msgCallback.subTopic());
                    //标记是否是回调消息的参数
                    if (handlerMethod.getMethodParameters().length > 0) {
                        Class<?> paramClass = handlerMethod.getMethodParameters()[0].getParameterType();
                        if (paramClass.getName().equals(MsgCallbackDTO.class.getName()) ) {
                            callbackFlag = "1";
                        }
                    }
                }
                requestMappingDTO.setDescription(description);
                requestMappingDTO.setPath(path);
                requestMappingDTO.setCode(md5);
                requestMappingDTO.setClassName(className);
                requestMappingDTO.setMethodName(methodName);
                requestMappingDTO.setMethod(methods);
                requestMappingDTO.setServiceId(serviceId);
                requestMappingDTO.setReturnType(returnName);
                requestMappingDTO.setPermissionFlag(permissionFlag);
                requestMappingDTO.setClientAuthorizationFlag(clientFlag);
                requestMappingDTO.setCallbackFlag(callbackFlag);
                requestMappingDTO.setInnerApi(innerApiFlag);
                list.add(requestMappingDTO);
            }
            requestMappingScanService.handle(serviceId,list);
            if (log.isDebugEnabled()) {
                log.info("资源扫描结果:serviceId=[{}] list=[{}] ", serviceId, list);
            }
        } catch (Exception e) {
            log.error("资源扫描有误", e);
        }

    }


    /**
     * swagger的请求不解析
     * @author Victor.Xiao
     * @since 2023-09-05 15:56
     * @param requestPath requestPath
     * @return boolean
    */
    private boolean isIgnore(String requestPath) {
        return AntPathMatcherUtil.match(requestPath, ignoreApi);
    }
}
